/*
 * Created on Sun Jun 10 2018 by Ronnie Ren (zhaosong.ren) from Accenture
 *
 * Copyright (c) 2018 DingXin information & technology limited company.
 */

import { HttpService, HTTPMethods, APIPath } from "http-service";
import { Injectable } from "@angular/core";
import { API } from "../../models/api-path-extension";
import { ResponseData } from "../../models/response-data";
import { Observable } from "rxjs/Observable";
import { CurdService } from "../../models/services/curd.service";
import { UserDto } from "../../models/dto/user.dto";
import { User } from "../../models/transform/user";
import { DistrictDto } from "../../models/dto/district.dto";
import { District } from "../../models/transform/district";
import { SysUserDto } from "../../models/dto/sysUser.dto";
import { SysUser } from "../../models/transform/sysUser";
import { WorkOrderDto } from "../../models/dto/work-orders.dto";
import { WorkOrder, WorkOrderType, WorkOrderStatus } from "../../models/transform/work-order";
import { StorageDto } from "../../models/dto/storage.dto";
import { CustomStorage } from "../../models/transform/custom-storage";
import { BillDto } from "../../models/dto/bill.dto";
import { Bill } from "../../models/transform/bill";
import { AreaDto } from "../../models/dto/area.dto";
import { Area } from "../../models/transform/area";
import { DeviceDto } from "../../models/dto/device.dto";
import { Device } from "../../models/transform/device";
import { SyncUtilService } from "./sync.util.service";
import { ObjectDto } from "../sqliteDB/object.dto";
import { ElectricityMeterDto } from "../../models/dto/electricity-meter.dto";
import { AttachmentDto } from "../../models/dto/attachment.dto";
import { StorageService } from "../../models/services/storage.service";
import { JSONMapping } from "../../models/json-mapping";
import { InspectionManualsDto } from "../../models/dto/inspection/inspection-manuals.dto";
import { InspectionTasksDto } from "../../models/dto/inspection/inspection-tasks.dto";
import { InspectionRecordsDto } from "../../models/dto/inspection/inspection-records.dto";
import { InspectionOperationsTicketsDto } from "../../models/dto/inspection/inspection-operation-tickets.dto";
import {ElectricityMeter} from "../../models/transform/electricity-meter";
import {Attachment} from "../../models/transform/attachment";
import { WorkTicketDto } from "../../models/dto/inspection/work-ticket.dto";
import { InspectionSysUserDto } from "../../models/dto/inspection/inspection-sysUser.dto";
import { SecureStorageService } from "../secure-storage.services";
import { UtilsService } from "../utils.services";
import { InspectionPersonnelDto } from "../../models/dto/inspection/inspection-personnel.dto";
import { InspectionCompanyDto } from "../../models/dto/inspection/inspection-company.dto";
import { InspectionTypeDto } from "../../models/dto/inspection/inspection-type.dto";
import {RoleStatus} from "../../models/role.status";
import {LastFetchingDate} from "../../models/lastFetchingDate";
import { UserFeature } from "../../models/transform/user-feature";
import { UserFeatureDto } from "../../models/dto/user-feature.dto";
import { UserCharacteristicRelationship } from "../../models/transform/user-characteristic-relationship";
import { UserCharacteristicRelationshipDto } from "../../models/dto/user-characteristic-relationship.dto";
import {Network} from "@ionic-native/network";
import {NetStatus, SyncPushService} from "./sync-push.service";


@Injectable()
export class SyncService {
  static pushProcess: any;
  pageSize: number = 50;
  customStorageData: Array<object>;
  constructor(
    private httpService: HttpService,
    private curdService: CurdService,
    private utilsService: UtilsService,
    private syncUtilService: SyncUtilService,
    private customeStorageService: StorageService,
    private network: Network,
    private syncPushService: SyncPushService

  ) {

  }

  openDatabase() {
    let allTables: Array<any> = [
      StorageDto,
      UserDto,
      AttachmentDto,
    ];
    let role = window.localStorage.getItem("role");
    let marketing = new RegExp(RoleStatus.marketing);
    let producer = new RegExp(RoleStatus.producer);
    if (marketing.test(role)) {
      allTables = allTables.concat(
        [
          SysUserDto,
          DistrictDto,
          WorkOrderDto,
          BillDto,
          AreaDto,
          DeviceDto,
          ElectricityMeterDto,
          UserFeatureDto,
          UserCharacteristicRelationshipDto]);
    }
    if (producer.test(role)) {
      allTables = allTables.concat(
        [
          // InspectionAttachmentsDto,
          InspectionManualsDto,
          InspectionTasksDto,
          InspectionRecordsDto,
          // InspectionOperationsDto,
          InspectionOperationsTicketsDto,
          WorkTicketDto,
          InspectionSysUserDto,
          InspectionPersonnelDto,
          InspectionCompanyDto,
          InspectionTypeDto]);
    }
    window.localStorage.removeItem("tableNames");
    return Observable.combineLatest(allTables.map((table: new () => ObjectDto) => {
      return Observable.fromPromise(this.curdService.createTable(table));
    }))
      .do(() => {
          if (SyncService.pushProcess) {
            clearInterval(SyncService.pushProcess);
          }
          SyncService.pushProcess = setInterval(() => {
          this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.pushTime)
            .subscribe((lastFetchingDate: any) => {
              if (!lastFetchingDate) {
                return ;
              }
              if (+new Date() - lastFetchingDate > 10 * 60 * 1000) {
                if (new RegExp(this.network.type).test(NetStatus.work)) {
                  this.syncPushService.startPush()
                    .subscribe(() => {
                      // TODO
                    });
                }
              }
            });
          }, 10 * 60 * 1000);
      },
        (error: any) => {
          //
        });
  }

  fetchAppData() {
    this.utilsService.showLoading();
    this.customeStorageService.fetchValueByKey("lastFetchingDate")
    .subscribe((val) => {
      let lastFetchingDate = null;
      if (val && val.length > 0) {
        lastFetchingDate = new Date(parseInt(val, 10));
      }
      // this.startFetch(lastFetchingDate);
    });
  }

  startFetch(): Observable<any> {
    // let lastFetchingDate = dateStr;
    let now = new Date().getTime();
    this.utilsService.showLoading();
    return Observable.combineLatest(
      this.remoteFetchSysUserData()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.sysUser, value: now.toString() });
        }),
      this.remoteFetchDistrictData()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.district, value: now.toString() });
        }),
      this.remoteFetchAreaData()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.area, value: now.toString() });
        }),
      // this.remoteFetchDeviceData()
      this.syncUtilService.getApiLastFetchingPage(LastFetchingDate.devicePage)
        .flatMap((lastFetchingPage) => {
          return this.remoteFetchDeviceData(lastFetchingPage);
        })
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.device, value: now.toString() });
        }),
      this.remoteFetchPowerCutInfoAndSave()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.getPowerCut, value: now.toString() });
        }),
      this.remoteFetchPowerRecoveryInfoAndSave()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.getRecover, value: now.toString() });
        }),
      this.remoteFetchUserFeatureData()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.userFeature, value: now.toString() });
        }),
      this.remoteFetchUserCharacteristicsData()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.userCharacteristics, value: now.toString() });
        }),
    )
      .flatMap(() => this.judgeStartPageFetchUnpaidUsersByPage())
      .flatMap(() => this.judgeStartPageFetchUsersPaidHistoryByPage())
      // .map(() => {
      //   this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.payHistory, value: now.toString() });
      // })
      .flatMap(() => this.remoteFetchWorkOrderByPageAndSave())
      .map((val) => {
        // TODO use the date returned from server.
        console.log(val, "----remote Date");
        this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.perform, value: now.toString() });

        let lastFetchingDateStorage = new CustomStorage({ key: "lastFetchingDate", value: now.toString() });
        this.syncUtilService.saveData(
          [this.syncUtilService.toObjectDto(lastFetchingDateStorage, StorageDto)],
          StorageDto,
          "key").subscribe();
      });
  }

  judgeStartPageFetchUnpaidUsersByPage() {
    return this.syncUtilService.getApiLastFetchingPage(LastFetchingDate.recoverPage)
      .flatMap((lastFetchingPage) => {
        return this.remoteFetchUnPaidUsersByPageAndSave( lastFetchingPage);
      });
  }

  judgeStartPageFetchUsersPaidHistoryByPage() {
    return this.syncUtilService.getApiLastFetchingPage(LastFetchingDate.payHistoryPage)
      .flatMap((lastFetchingPage) => {
        return this.remoteFetchUnPaidUsersPaidHistoryByPageAndSave( lastFetchingPage);
      });
  }

  fetchAllData(lastFetchingDate: Date, api): Observable<any> {
    // return this.httpService.fetchWithPath(api, {lastFetchingDate: lastFetchingDate.getTime()});
    return this.httpService.fetchWithPath(api);
  }

  remoteFetchPowerCutInfoAndSave(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.getPowerCut)
      .flatMap((lastFetchingDate) => {
        return this.remoteFetchWorkOrderInfoAndSave(
          lastFetchingDate,
          new API().path({path: "getPowerCut", method: HTTPMethods.Get}), WorkOrderType.POWERCUT, "powerCut");
      });
  }

  remoteFetchPowerRecoveryInfoAndSave(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.area)
      .flatMap((lastFetchingDate) => {
        return this.remoteFetchWorkOrderInfoAndSave(
          lastFetchingDate,
          new API().path({path: "getRecover", method: HTTPMethods.Get}), WorkOrderType.POWERRECOVERY, "recover");
      });
  }

  remoteFetchWorkOrderInfoAndSave<T extends ObjectDto>(
    lastFetchingDate: Date, apiPath: APIPath, workOrderType: WorkOrderType, apiType: string): Observable<boolean> {

    return this.syncUtilService.remoteFetchWithArray(
      lastFetchingDate,
      apiPath,
      WorkOrder)
      .do((workOrders: Array<WorkOrder>) => {
        console.log(JSON.stringify(workOrders), "workOrder");
        workOrders.forEach((workOrder) => {
          let applyStatus: number;
          workOrder.type = workOrderType;
          if (apiType === "powerCut") {
            applyStatus = workOrder["powerCutApplyStatus"];
          }
          else {
            applyStatus = workOrder["recoverApplyStatus"];
          }
          switch (applyStatus) {
            // case 0 : workOrder.workOrderStatus = WorkOrderStatus.TO_BE_SUBMIT; break;
            case 1: workOrder.workOrderStatus = WorkOrderStatus.TO_BE_APPROVED; break;
            case 2: workOrder.workOrderStatus = WorkOrderStatus.APPROVED; break;
            case 3: workOrder.workOrderStatus = WorkOrderStatus.REJECTED; break;
            default: break;
          }
          if (apiType === "powerCut") {
            if (workOrder["powerCutStatus"]) {
              workOrder.workOrderStatus = WorkOrderStatus.EXECUTED;
            }
          }
          else {
            if (workOrder["recoverStatus"]) {
              workOrder.workOrderStatus = WorkOrderStatus.EXECUTED;
            }
          }
          if (!workOrder.id) {
            workOrder.workOrderStatus = WorkOrderStatus.TO_BE_SUBMIT;
          }
        });
      })
      .map((workOrders: Array<WorkOrder>) => workOrders.map((order) => {
        return this.syncUtilService.toObjectDto(order, WorkOrderDto);
      }))
      .flatMap((dtos: Array<WorkOrderDto>) => {
        // console.log(JSON.stringify(dtos));
        if (apiType === "powerCut") {
          return this.syncUtilService.saveData(dtos, WorkOrderDto, "id,applyId,chargeDetailId");
        } else {
          let existCheckingPropertyKey = "payDate,chargeDetailId";
          return this.syncUtilService.fetchPropertyWithArray(WorkOrderDto, existCheckingPropertyKey)
            .flatMap((existingPropertys: Array<string>) => {
              let existCheckingPropertyKeyArray: Array<string> = [];
              let dtoExistCheckingPropertyValue: string = "";
              if (existingPropertys && existingPropertys.length > 0) {
                dtos = dtos.filter((dto: WorkOrderDto) => {
                  existCheckingPropertyKeyArray = existCheckingPropertyKey.split(",");
                  existCheckingPropertyKeyArray.forEach((existCheckingPropertyKeyItem) => {
                    dtoExistCheckingPropertyValue += dto[existCheckingPropertyKeyItem];
                  });
                  let exist = existingPropertys.indexOf(dtoExistCheckingPropertyValue) >= 0;
                  dtoExistCheckingPropertyValue = "";
                  return !exist;
                });
              }
              return this.syncUtilService.saveData(dtos, WorkOrderDto, existCheckingPropertyKey);
            });

        }
      })
      .map(() => true);
  }

  remoteFetchUnPaidUsersPaidHistoryByPageAndSave(
    page: number,
    pageSize: number = this.pageSize):
    Observable<boolean> {
    // let fetchingDate: Date = null;
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.payHistory)
      .flatMap((lastFetchingDate) => {
        return this.syncUtilService.remoteFetchWithArray(lastFetchingDate, new API().path({path: "payHistory"}), User, page, pageSize)
          .do((users: Array<User>) => {
            console.log(users, JSON.stringify(users));
            users.forEach((user) => {
              if (user.bills) {
                user.bills.forEach((bill) => {
                  bill.userId = user.id;
                });
              }
            });
          })
          .flatMap((users: Array<User>) => {
            let ob: Observable<boolean>;
            if (users.length >= pageSize) {
              // fetch next page & save data
              ob = this.remoteFetchUnPaidUsersPaidHistoryByPageAndSave(page + 1, pageSize);
            }
            else {
              // finish & exit
              // ob = Observable.of(true);
              ob = Observable.create((observaber) => {
                this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.payHistory, value: new Date().getTime().toString() });
                observaber.next();
                observaber.complete();
              })
                .flatMap(() => {
                  return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.payHistoryPage, value: "1" });
                })
                .map(() => Observable.of(true));
            }
            return this.saveUsersAndBills(users, true)
              .flatMap(() => {
                return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.payHistoryPage, value: page.toString() });
              })
              .flatMap(() => ob);
          });
      });
  }

  remoteFetchWorkOrderByPageAndSave<T extends ObjectDto, M extends JSONMapping>(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.perform)
      .flatMap((lastFetchingDate) => {
        let checkingPropertyKey = "type,chargeDetailId";
        return this.remoteFetchDataAndMapping(
          lastFetchingDate,
          new API().path({path: "perform", method: HTTPMethods.Get}),
          checkingPropertyKey,
          WorkOrder,
          WorkOrderDto)
          .flatMap((dtos: Array<WorkOrderDto>) => {
            dtos = dtos.map((dto) => {
              dto.workOrderStatus = WorkOrderStatus.EXECUTED;
              return dto;
            });
            return this.syncUtilService.saveData(dtos, WorkOrderDto, checkingPropertyKey)
              .map(() => {
                return dtos;
              });
          })
          // .flatMap((dtos: Array<WorkOrderDto>) => {
          //   let attachmentArray = [];
          //   dtos.map((dto) => {
          //     attachmentArray = attachmentArray.concat(
          //       Attachment.useAttachmentIdsAndThumbnailIdsGetAttachments(dto.powerCutPhotoIds, dto.powerCutThrumnailIds));
          //   });
          //   attachmentArray = this.syncUtilService.downImageAndGetLocalUrl(attachmentArray);
          //   if (attachmentArray.length > 0) {
          //     return Observable.combineLatest(attachmentArray)
          //       .flatMap((attachments: Array<Attachment>) => {
          //         return this.syncUtilService.saveAttachment(attachments);
          //       });
          //   } else {
          //     return Observable.of(false);
          //   }
          // })
          .map(() => true);
      });
  }

  remoteFetchUnPaidUsersByPageAndSave(
    page: number,
    pageSize: number = this.pageSize):
    Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.customer)
      .flatMap((lastFetchingDate) => {
      return this.syncUtilService.remoteFetchWithArray(lastFetchingDate, new API().path({ path: "customer" }), User, page, pageSize)
        .do((users: Array<User>) => {
          console.log(users, JSON.stringify(users));
          users.forEach((user) => {
            if (user.bills) {
              let chargeDetailId: string = "";
              user.bills.forEach((bill) => {
                bill.userId = user.id;
                chargeDetailId += `${bill.chargeDetailId},`;
                if (user.theToppestChargeLevel === 2) {
                  if (bill.noticeUploadTime) {
                    user.noticeUploadTime = bill.noticeUploadTime;
                  }
                }
              });
              user.chargeDetailIds = chargeDetailId.substr(0, chargeDetailId.length - 1);
            }
          });
        })
        .flatMap((users: Array<User>) => {
        let ob: Observable<boolean>;
        if (users.length >= pageSize) {
          // fetch next page & save data
          ob = this.remoteFetchUnPaidUsersByPageAndSave(page + 1, pageSize);
        }
        else {
          // finish & exit
          ob = Observable.create((observaber) => {
            this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.customer, value: new Date().getTime().toString() });
            observaber.next();
            observaber.complete();
          })
            .flatMap(() => {
              return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.recoverPage, value: "1" });
            })
            .map(() => Observable.of(true));
        }
        return this.saveUsersAndBills(users)
          .flatMap(() => {
            return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.recoverPage, value: page.toString() });
          })
          .flatMap(( ) => ob);
      });
    });
  }

  saveBills(bills: Array<Bill>, isPaidBills: boolean): Observable<any> {
    let billDtos = bills.map((bill: Bill) => {
      let billDto = this.syncUtilService.toObjectDto(bill, BillDto);
      // id is a calculated property, we have to call it to get it created first time.
      billDto.id = bill.id;
      billDto.isSettle = isPaidBills ? 1 : 0;
      return billDto;
    });
    return this.syncUtilService.saveData(billDtos, BillDto, "id");
  }

  private saveElectricityMeter(electricityMeters: Array<ElectricityMeter>, bill: Bill): Observable<boolean> {
    let electricityMeterDtos = electricityMeters.map((electricityMeter) => {
      let electricityMeterDto = this.syncUtilService.toObjectDto(electricityMeter, ElectricityMeterDto);
      electricityMeterDto.billId = bill.id;
      electricityMeterDto.userId = bill.userId;
      electricityMeterDto.billDate = bill.billDate;
      electricityMeterDto.arrearsTotal = bill.totalFee;
      return electricityMeterDto;
    });
    if (electricityMeterDtos.length > 0) {
      return this.syncUtilService.saveData(electricityMeterDtos, ElectricityMeterDto, "meterId,billId");
    } else {
      return Observable.of(false);
    }
  }



  private saveUsersAndBills(users: Array<User>, isPaidBills: boolean = false): Observable<boolean> {
    let userDtos = users.map((user) => this.syncUtilService.toObjectDto(user, UserDto));
    let attachmentArray: Array<any> = [];
    return this.syncUtilService.saveData(userDtos, UserDto, "id", isPaidBills)
      .flatMap(() => {
        let saveBillObs: Array<any> = users.map((user) => this.saveBills(user.bills, isPaidBills));
        let saveElectricityMeterArray: Array<any> = [];
        console.log(users, JSON.stringify(users));
        users.forEach((user) => {
          user.bills.forEach((item) => {
            if (this.saveElectricityMeter(item.consumedElectricitys, item)) {
              saveElectricityMeterArray.push(this.saveElectricityMeter(item.consumedElectricitys, item));
            }
            // attachmentArray =
            // attachmentArray.concat(Attachment.useAttachmentIdsAndThumbnailIdsGetAttachments(item.attachmentIds, item.thumbnailIds));
          });
        });
        // attachmentArray = this.syncUtilService.downImageAndGetLocalUrl(attachmentArray);
        if (saveBillObs && saveBillObs.length > 0) {
          return Observable.combineLatest(saveBillObs)
            .flatMap(() => {
              if (saveElectricityMeterArray.length > 0) {
                return Observable.combineLatest(saveElectricityMeterArray);
              } else {
                return Observable.of([]);
              }
            });
        }
        return Observable.of([]);
      })
    // .flatMap(() => {
    //   if (attachmentArray.length > 0) {
    //     return Observable.combineLatest(attachmentArray)
    //       .flatMap((attachments: Array<Attachment>) => {
    //         return this.syncUtilService.saveAttachment(attachments);
    //       });
    //   } else {
    //     return Observable.of(false);
    //   }
    // })
    .map(() => true);
  }

  private remoteFetchDeviceData(page: number,
                                pageSize: number = this.pageSize): Observable<any> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.device)
      .flatMap((lastFetchingDate) => {
    //     return this.syncUtilService.remoteFetchWithArray(lastFetchingDate,
    //       new API().path({path: "device", method: HTTPMethods.Get}), Device, page, pageSize)
    //       .flatMap((devices: Array<Device>) => {
    //         let ob: Observable<boolean>;
    //         if (devices.length >= pageSize) {
    //           // fetch next page & save data
    //           ob = this.remoteFetchDeviceData(page + 1, pageSize);
    //         }
    //         else {
    //           // finish & exit
    //           ob = Observable.create((observaber) => {
    //             this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.device, value: new Date().getTime().toString() });
    //             observaber.next();
    //             observaber.complete();
    //           })
    //             .flatMap(() => {
    //               return this.updateStoragePage({ key: LastFetchingDate.devicePage, value: "1" });
    //             })
    //             .map(() => Observable.of(true));
    //         }
    //         return this.saveDataByModel(devices, DeviceDto, "id,componentId")
    //           .flatMap(() => {
    //             return this.updateStoragePage({ key: LastFetchingDate.devicePage, value: page.toString() });
    //           })
    //           .flatMap(( ) => ob);
    //       });
        let date = new Date();
        let shouldUpdateDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-01 00:00`);
        if (lastFetchingDate && lastFetchingDate > shouldUpdateDate) {
          return Observable.of(true);
        } else {
          return this.remoteFetchData(
            lastFetchingDate,
            new API().path({path: "device", method: HTTPMethods.Get}),
            "id,componentId",
            Device,
            DeviceDto);
        }

      });
  }

  private saveDataByModel<T extends ObjectDto>(models, dtoConstructorFunc, existCheckingPropertyKey) {
    console.log(JSON.stringify(models), dtoConstructorFunc, "deguber 1111111111111111");
    let dtos: Array<T> =  models.map((model) => {
      console.log(JSON.stringify(models), "deguber 1111");
      return this.syncUtilService.toObjectDto(model, dtoConstructorFunc);
    });
    return this.syncUtilService.saveData(dtos, dtoConstructorFunc, existCheckingPropertyKey);
  }

  private remoteFetchAreaData(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.area)
      .flatMap((lastFetchingDate) => {
        let date = new Date();
        let shouldUpdateDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-01 00:00`);
        if (lastFetchingDate && lastFetchingDate > shouldUpdateDate) {
          return Observable.of(true);
        } else {
          return this.remoteFetchData(
            lastFetchingDate,
            new API().path({path: "area", method: HTTPMethods.Get}),
            "id",
            Area,
            AreaDto);
        }
      });
  }

  private remoteFetchDistrictData(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.district)
      .flatMap((lastFetchingDate) => {
        let date = new Date();
        let shouldUpdateDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-01 00:00`);
        if (lastFetchingDate && lastFetchingDate > shouldUpdateDate) {
          return Observable.of(true);
        } else {
          return this.remoteFetchData(
            lastFetchingDate,
            new API().path({path: "district", method: HTTPMethods.Get}),
            "id",
            District,
            DistrictDto);
        }
      });
  }

  private remoteFetchUserFeatureData(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.district)
    .flatMap((lastFetchingDate) => {
      return this.remoteFetchData(
        lastFetchingDate,
        new API().path({ path: "userCharacteristics", method: HTTPMethods.Get }),
        "markNo",
        UserFeature,
        UserFeatureDto);
      });
  }

  private remoteFetchUserCharacteristicsData(): Observable<boolean> {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.district)
    .flatMap((lastFetchingDate) => {
      return this.remoteFetchData(
        lastFetchingDate,
        new API().path({ path: "userCharacteristicsRelation", method: HTTPMethods.Post }),
        "customerNo,markNo",
        UserCharacteristicRelationship,
        UserCharacteristicRelationshipDto);
    });
  }



  private remoteFetchSysUserData() {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.sysUser)
      .flatMap((lastFetchingDate) => {
        let date = new Date();
        let shouldUpdateDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-01 00:00`);
        if (lastFetchingDate && lastFetchingDate > shouldUpdateDate) {
          return Observable.of(true);
        } else {
          return this.remoteFetchData(
            lastFetchingDate,
            new API().path({path: "sysUser", method: HTTPMethods.Get}),
            "account",
            SysUser,
            SysUserDto);
        }
      });
  }

  private remoteFetchData<T extends ObjectDto, M extends JSONMapping>(
    lastFetchingDate: Date,
    apiPath: APIPath,
    existCheckingPropertyKey: string,
    modelConstructorFunc: { new(json: object): M },
    dtoConstructorFunc: { new(): T }): Observable<boolean> {

    return this.syncUtilService.remoteFetchWithArray(
      lastFetchingDate,
      apiPath,
      modelConstructorFunc)
      .map((models) => {
        console.log(JSON.stringify(models), modelConstructorFunc, "debuger 222222222222");
        return models.map((model) => {
          console.log(JSON.stringify(models), "debuger 222222");
          return this.syncUtilService.toObjectDto(model, dtoConstructorFunc);
        })
      })
      .flatMap((dtos: Array<T>) => {
        return this.syncUtilService.saveData(dtos, dtoConstructorFunc, existCheckingPropertyKey);
      })
      .map(() => {
        // console.log("r3rewqr3rwqfefwqr----");
        return true;
      });
  }

  private remoteFetchDataAndMapping<T extends ObjectDto, M extends JSONMapping>(
    lastFetchingDate: Date,
    apiPath: APIPath,
    existCheckingPropertyKey: string,
    modelConstructorFunc: { new(json: object): M },
    dtoConstructorFunc: { new(): T }) {

    return this.syncUtilService.remoteFetchWithArray(
      lastFetchingDate,
      apiPath,
      modelConstructorFunc)
      .map((models) => {
        console.log(models, modelConstructorFunc, 'debuger 33333333333333333333');
        return models.map((model) => {
          console.log(model, 'debuger 33333');
          return this.syncUtilService.toObjectDto(model, dtoConstructorFunc);
        })
      });
  }

}


